﻿using Framework.Core.Consts;
using Framework.Core.Options;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using my.abp.Domain.Shared.Entities;
using my.abp.Domain.Shared.Etos;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids;
using Volo.Abp.Security.Claims;
using Volo.Abp;
using my.abp.Domain.IManagers;
using Framework.Core.Helper;
using my.abp.Domain.Shared.Consts;
using my.abp.Domain.Shared.Entities;
using my.abp.Domain.Repositories.User;

namespace my.abp.Domain.Managers
{
    /// <summary>
    /// 用户领域服务
    /// </summary>
    public class AccountManager : DomainService, IAccountManager
	{
		private readonly ISysUserRepository _repository;
		private readonly JwtOptions _jwtOptions;
		private readonly RefreshJwtOptions _refreshJwtOptions;

		public AccountManager(
			IOptions<JwtOptions> jwtOptions,
			IOptions<RefreshJwtOptions> refreshJwtOptions, 
			ISysUserRepository repository)
		{
			_jwtOptions = jwtOptions.Value;
			_refreshJwtOptions = refreshJwtOptions.Value;
			_repository = repository;
		}

		/// <summary>
		/// 登录效验
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="password"></param>
		/// <param name="userAction"></param>
		/// <returns></returns>
		public async Task LoginValidationAsync(string userName, string password, Action<SysUser> userAction = null)
		{
			var user = new SysUser();
			if (await ExistAsync(userName, o => user = o))
			{
				if (userAction is not null)
				{
					userAction.Invoke(user);
				}
				if (user.Password == MD5Encryption.Encrypt(password, true, false))
				{
					return;
				}
				throw new UserFriendlyException(UserConst.Login_Error);
			}
			throw new UserFriendlyException(UserConst.Login_User_No_Exist);
		}

		/// <summary>
		/// 获取token
		/// </summary>
		/// <param name="userId"></param>
		/// <returns></returns>
		/// <exception cref="UserFriendlyException"></exception>
		public async Task<string> GetToken(SysUser user)
		{
			var accessToken = CreateToken(this.UserInfoToClaim(user));
			//将用户信息添加到缓存中，需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新，看具体业务进行选择

			return accessToken;
		}

		/// <summary>
		/// 获取token
		/// </summary>
		/// <param name="userId"></param>
		/// <returns></returns>
		/// <exception cref="UserFriendlyException"></exception>
		public async Task<string> GetRefreshToken(SysUser user)
		{
			var accessToken = CreateRefreshToken(this.UserInfoToClaim(user));
			//将用户信息添加到缓存中，需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新，看具体业务进行选择

			return accessToken;
		}

		private void AddToClaim(List<KeyValuePair<string, string>> claims, string key, string value)
		{
			claims.Add(new KeyValuePair<string, string>(key, value));
		}

		/// <summary>
		/// 令牌转换
		/// </summary>
		/// <param name="dto"></param>
		/// <returns></returns>
		private List<KeyValuePair<string, string>> UserInfoToClaim(SysUser user)
		{
			var claims = new List<KeyValuePair<string, string>>();
			//AddToClaim(claims, AbpClaimTypes.UserId, Guid.NewGuid().ToString());
			//AddToClaim(claims, "Id", user.Id.ToString());
			AddToClaim(claims, AbpClaimTypes.UserId, user.Id.ToString());
			AddToClaim(claims, ClaimConst.Account, user.Account);
			AddToClaim(claims, ClaimConst.Name, user.Name);
			AddToClaim(claims, ClaimConst.OrgId, user.OrgId.ToString());
			AddToClaim(claims, ClaimConst.OrgName, user.SysOrg?.Name);
			return claims;
		}

		/// <summary>
		/// 创建令牌
		/// </summary>
		/// <param name="kvs"></param>
		/// <returns></returns>
		private string CreateToken(List<KeyValuePair<string, string>> kvs)
		{
			var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.SecurityKey));
			var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
			var claims = kvs.Select(x => new Claim(x.Key, x.Value)).ToList();
			var token = new JwtSecurityToken(
			   issuer: _jwtOptions.Issuer,
			   audience: _jwtOptions.Audience,
			   claims: claims,
			   expires: DateTime.Now.AddMinutes(_jwtOptions.ExpiresMinuteTime),
			   notBefore: DateTime.Now,
			   signingCredentials: creds);
			string returnToken = new JwtSecurityTokenHandler().WriteToken(token);

			return returnToken;
		}

		/// <summary>
		/// 创建令牌
		/// </summary>
		/// <param name="kvs"></param>
		/// <returns></returns>
		private string CreateRefreshToken(List<KeyValuePair<string, string>> kvs)
		{
			var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_refreshJwtOptions.SecurityKey));
			var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
			var claims = kvs.Select(x => new Claim(x.Key, x.Value.ToString())).ToList();
			var token = new JwtSecurityToken(
			   issuer: _refreshJwtOptions.Issuer,
			   audience: _refreshJwtOptions.Audience,
			   claims: claims,
			   expires: DateTime.Now.AddMinutes(_refreshJwtOptions.ExpiresMinuteTime),
			   notBefore: DateTime.Now,
			   signingCredentials: creds);
			string returnToken = new JwtSecurityTokenHandler().WriteToken(token);

			return returnToken;
		}

		/// <summary>
		/// 判断账户合法存在
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="userAction"></param>
		/// <returns></returns>
		public async Task<bool> ExistAsync(string userName, Action<SysUser> userAction = null)
		{
			var user = await _repository.GetFirstAsync(u => u.Account == userName);
			if (userAction is not null)
			{
				userAction.Invoke(user);
			}
			//这里为了兼容解决数据库开启了大小写不敏感问题,还要将用户名进行二次效验
			if (user != null && user.Account == userName)
			{
				return true;
			}
			return false;
		}
	}
}
